/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package actions

import (
	"gitee.com/tugoer/orivil/mux"
	"io/ioutil"
	"path/filepath"
	"os"
	"image"
	"image/png"
	"sync"
	"github.com/disintegration/imaging"
	"image/color"
	"github.com/golang/freetype/truetype"
	"github.com/golang/freetype"
	"image/draw"
	"gitee.com/tugoer/orivil/log"
	"gitee.com/tugoer/psy-testing/code/api/tags"
)

const (
	FontLibraryDir = "library/fonts"
	FontExampleImageDir = "images/font-example"
)

func init() {
	err := os.MkdirAll(FontLibraryDir, 0777)
	if err != nil {
		panic(err)
	}
	err = os.MkdirAll(FontExampleImageDir, 0777)
	if err != nil {
		panic(err)
	}

	list, err := readFontList()
	if err != nil {
		panic(err)
	}
	checkFontList(list, func(fontFile string, err error) {
		file := filepath.Join(FontLibraryDir, fontFile)
		log.Info.Printf("删除出错的字体：%s\n", fontFile)
		os.Remove(file)
	})
}

func readFontList() (list []string, err error) {
	fs, e := ioutil.ReadDir(FontLibraryDir)
	if e != nil {
		return nil, e
	}
	var fonts []string
	for _, f := range fs {
		fonts = append(fonts, f.Name())
	}
	return fonts, nil
}

func checkFontList(ul []string, handleError func(fontFile string, err error)){
	for _, li := range ul {
		font, err := fontContainer.GetFace(li, 20)
		if err != nil {
			panic(err)
		}
		_, err = DrawText("示例", 20, color.Black, color.Transparent, font)
		if err != nil {
			handleError(li, err)
		}
	}
}

var Fonts = []*mux.Action{
	{
		Name: "Get Font List",
		Method: "GET",
		Route: "/fonts",
		Handler: func(ctx *mux.Context) {
			fonts, err := readFontList()
			ctx.ErrWarning(err)
			ctx.SendJson(fonts)
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tags.Font},
			Summary: "获得字体列表",
			Responses:mux.Responses{
				"200": {
					Description:"success",
					Example: []string{"Deng.ttf"},
				},
			},
		},
	},
	{
		Name: "Generate Font Image",
		Method: "GET",
		Route: "/fonts/generate-image",
		Params:mux.Parameters{
			{
				In:mux.Query,
				Schema: &generateImage{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &generateImage{}
			ctx.WarnParse(p)
			fontFace, err := fontContainer.GetFace(p.Font, p.FontSize)
			ctx.ErrWarning(err)
			rgba := color.RGBA{
				R: uint8(p.FrontR),
				G: uint8(p.FrontG),
				B: uint8(p.FrontB),
				A: uint8(p.FrontA),
			}
			img, err := DrawText(p.Text, p.FontSize, rgba, color.Transparent, fontFace)
			if p.Blur > 0 {
				n := imaging.Blur(img, p.Blur)
				img = imaging.Paste(img, n, image.Pt(0, 0))
			}
			ctx.ErrWarning(err)
			ctx.Response.Header().Set("Content-Type", "image/png")
			err = png.Encode(ctx.Response, img)
			ctx.ErrWarning(err)
		},
		ApiDoc:&mux.ApiDoc {
			Tags:[]string{tags.Font},
			Summary: "生成字体图片",
			//Responses:orivil.Responses{
			//	"200": {
			//		Description:"success",
			//		Example: ,
			//	},
			//},
		},
	},
}

type generateImage struct {
	Text string `desc:"需要转换的字符串" required:"需要转换的字符串不能为空"`
	Font string `desc:"字体" required:"未设置字体"`
	FontSize int `desc:"字体大小"`
	Cache bool `desc:"是否缓存当前生成的图片"`
	FrontR int `desc:"红色通道" num:"0<=x<=255" num-msg:"必须在0-255之间"`
	FrontG int `desc:"绿色通道" num:"0<=x<=255" num-msg:"必须在0-255之间"`
	FrontB int `desc:"蓝色通道" num:"0<=x<=255" num-msg:"必须在0-255之间"`
	FrontA int `desc:"透明度" num:"0<=x<=255" num-msg:"必须在0-255之间"`
	Blur float64 `desc:"模糊度" num:"0<=x<=10" num-msg:"必须在0-10之间"`
}

type rgba struct {
	R int
	G int
	B int
	A int
}

type fontFaceContainer struct {
	data  map[string]map[int]*truetype.Font
	dir string
	sync.Mutex
}

func (f *fontFaceContainer)GetFace(fontName string, size int) (font *truetype.Font, err error) {
	f.Lock()
	defer f.Unlock()
	if font, ok := f.data[fontName][size]; ok {
		return font, nil
	} else {
		b, err := ioutil.ReadFile(filepath.Join(f.dir, fontName))
		if err != nil {
			return nil, err
		}
		font, err = truetype.Parse(b)
		if err != nil {
			return nil, err
		} else {
			if _, ok := f.data[fontName]; !ok {
				f.data[fontName] = map[int]*truetype.Font{size: font}
			} else {
				f.data[fontName][size] = font
			}
			return font, nil
		}
	}
}

var fontContainer = &fontFaceContainer{
	data: make(map[string]map[int]*truetype.Font, 30),
	dir: FontLibraryDir,
}

//func DrawText2(text string, font string, fontSize int, rgba *rgba) (img image.Image, err error) {
//	with := len([]rune(text)) * fontSize
//	height := fontSize + 10
//	dc := gg.NewContext(with, height)
//
//	if fontFace, err := fontContainer.GetFace(font, fontSize); err != nil {
//		return nil, err
//	} else {
//		dc.SetFontFace(fontFace)
//		dc.SetRGBA255(rgba.R, rgba.G, rgba.B, rgba.A)
//		dc.DrawStringAnchored(text, float64(with/2), (float64(height))/2 - float64(fontSize/5), 0.5, 0.5)
//		dc.Clip()
//		return dc.Image(), nil
//	}
//}

func DrawText(text string, fontSize int, front, back color.Color, font *truetype.Font) (img image.Image, err error) {
	fg, bg := image.NewUniform(front), image.NewUniform(back)
	ctx := freetype.NewContext()
	ctx.SetFont(font)
	ctx.SetFontSize(float64(fontSize))
	ctx.SetSrc(fg)
	rgba := image.NewRGBA(image.Rect(0, 0, fontSize*len([]rune(text)), fontSize + fontSize/3))
	draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
	ctx.SetClip(rgba.Bounds())
	ctx.SetDst(rgba)
	pt, e := ctx.DrawString(text, freetype.Pt(0 , fontSize))
	if e != nil {
		return nil, e
	}
	return rgba.SubImage(image.Rect(0, 0, int(pt.X/64), fontSize + fontSize/3)), nil
}